package com.acme.javaschool.sems.ionio.net;

import com.acme.javaschool.sems.ionio.filetools.IoTools;
import com.acme.javaschool.sems.ionio.filetools.NioTools;

import java.io.*;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

import static java.lang.Math.max;
import static java.lang.Math.min;
import static java.lang.Thread.sleep;

public class FileServer {

    private static int CHUNK_SIZE = 32000;

    public void workIo(String acceptedFilePath, int port) {
        try (ServerSocket serverSocket = new ServerSocket(port);
             Socket socket = serverSocket.accept();
             DataInputStream in = new DataInputStream(socket.getInputStream());
             DataOutputStream out = new DataOutputStream(socket.getOutputStream());
             BufferedOutputStream fileOut = new BufferedOutputStream(new FileOutputStream(acceptedFilePath));
        ) {
            CHUNK_SIZE = in.readInt();
            int bytesTransferred = 0;
            int fileSize = in.readInt();
            System.out.println("[server][workIo] file size = " + fileSize);
            System.out.println("[server][workIo] transferring...");

            while (bytesTransferred < fileSize) {
                int bytesLast = IoTools.transferChunkIo(in, fileOut, min(CHUNK_SIZE, fileSize - bytesTransferred));
                out.writeInt(bytesLast);
                bytesTransferred += bytesLast;
            }

            System.out.println("[server][workIo] Done.");
        } catch (IOException e) { e.printStackTrace(); }
    }

    public void workNio(String acceptedFilePath, int port) {
        try (
             ServerSocketChannel serverSocketChannel = ServerSocketChannel.open().bind(new InetSocketAddress(port));
             SocketChannel socketChannel = serverSocketChannel.accept();
             FileChannel fileChannel = new FileOutputStream(new File(acceptedFilePath)).getChannel();
        ) {
            CHUNK_SIZE = (int) NioTools.readLong(socketChannel);
            long fileSize = NioTools.readLong(socketChannel);
            int bytesTransferred = 0;
            System.out.println("[server][workNio] Started. Transferring " + fileSize + " bytes.");

            while (bytesTransferred < fileSize) {
                int bytesLast = NioTools.receiveChunkAndPutToFileNio(socketChannel, fileChannel, (int) min(CHUNK_SIZE, fileSize - bytesTransferred));
                NioTools.writeLong(socketChannel, max(bytesLast, 0));
                bytesTransferred += bytesLast;
            }

            System.out.println("[server][workIo] Done.");
        } catch (IOException e) { e.printStackTrace(); }
    }

    public static void main(String... args) {
        final String mode = args[0];
        final String writeToPath = args[1];
        final Integer port = Integer.valueOf(args[2]);
        System.out.println("[server] Started.");

        if ("io".equals(mode)) {
            new FileServer().workIo(writeToPath, port);
        } else if ("nio".equals(mode)) {
            new FileServer().workNio(writeToPath, port);
        } else {
            System.out.println("[server] Unknown mode: " + mode + ".");
        }
    }
}
